import createDOMElement from "./createDOMElement";
import mountElement from "./mountElement";
import unmountNode from "./unmountNode";
import updateNodeElement from "./updateNodeElement";
import updateTextNode from "./updateTextNode";
import diffComponent from "./diffComponent";

export default function diff(vDom, container, oldDom) {
  // 获取旧节点的VDOM
  const oldVDom = oldDom && oldDom._vDom
  // 获取更新前的组件实例
  const oldComponent = oldVDom && oldVDom.component
  // 如果旧节点不存在，直接去渲染vDom
  if (!oldDom) {
    mountElement(vDom, container)
  } 
  // 如果新的vDOM是组件
  else if (typeof vDom.type === 'function') {
    diffComponent(vDom, oldComponent, oldDom, container)
  }
  // 如果vDom不是组件
  else if (typeof vDom.type !== 'function') {
    // 如果新的vDom和旧的vDom的type相同，说明是相同节点，可以进行比对
    if (oldVDom && vDom.type === oldVDom.type){
      // 文本节点，更新内容
      if (vDom.type === 'text') {
        updateTextNode(vDom, oldVDom, oldDom)
      }
      // 元素节点，更新属性
      else {
        updateNodeElement(oldDom, vDom, oldVDom)
      }

      // 通过key的值来更新子节点
      /**
       * 1. 将拥有key属性的子元素存放在一个单独的对象中
       * 2. 如果旧节点的子元素拥有key属性，将这个元素的DOM存放在这个对象中
       * 3. 判断存放拥有key属性的元素对象为空对象，则使用索引来更新子元素
       * 4. 如果对象不为空
       *    1. 循环新的vDom的子元素，获取子元素的key属性
       *    2. 判断位置上的元素是否是期望的元素，如果不是，使用insertBefore添加这个元素
       */
      
      let keyedElement = {}
      // 遍历旧节点子元素
      oldDom.childNodes.forEach(child => {
        // 如果是元素类型，获取key属性值
        if (child.nodeType === 1) {
          let key = child.getAttribute('key')
          if (key) {
            keyedElement[key] = child
          }
        }
      })
      let hasKeyedElement = Object.keys(keyedElement).length
      // 如果没有具有key属性的元素，则使用索引进行子节点更新
      if (hasKeyedElement === 0) {
        // 递归vDom的子元素进行更新
        vDom.children.forEach((child, i) => {
          diff(child, oldDom, oldDom.childNodes[i])
        })
      } else {
        // 遍历vDom的子节点
        vDom.children.forEach((child, i) => {
          let key = child.props.key
          if (key) {
            let element = keyedElement[key]
            if (element) {
              // 判断oldDom对应位置的元素和存储的keyDom对象中的元素是否一致
              if (oldDom.childNodes[i] && element !== oldDom.childNodes[i]) {
                // 不一致，将这个元素添加到oldDom对应的元素之前，这样oldDom对应的位置就变成了这个插入的元素
                oldDom.insertBefore(element, oldDom.childNodes[i])
              }
            } else {
              console.log(vDom,oldDom, 'befroe')
              // // 如果找不到这个element，说明当前的key对应的子节点是新增的
              mountElement(child, oldDom, oldDom.childNodes[i])
            }
          }
        })
      }

      // 删除节点发生在更新节点之后
      let oldChildren = oldDom.childNodes
      // 如果旧节点的子节点大于新的VDOM的子节点数量，则从后开始遍历删除
      if (oldChildren.length > vDom.children.length) {
        // 如果不存在带key的元素，则使用索引去删除节点
        if (hasKeyedElement === 0) {
          for(let i = oldChildren.length - 1; i > vDom.children.length - 1; i--) {
            unmountNode(oldChildren[i])
          }
        } else {
          for (let i = 0; i < oldChildren.length; i++) {
            // 如果一轮循环没有找到与vDom子节点的key相同的节点，则删除当前oldDom的子节点
            let oldChild = oldChildren[i]
            let oldKey = oldChild._vDom.props.key
            let found = false
            for(let j = 0; j < vDom.children.length; j++) {
              if(oldKey === vDom.children[j].props.key) {
                found = true
                break
              }
            } 
            if (!found) {
              unmountNode(oldChild)
            }
          }
        }
      }
      
    }
    // 如果新vDom和旧vDom的type不相同，直接用新的vDom生成DOM对象，替换旧的DOM
    else if (oldVDom && vDom.type !== oldVDom.type) {
      const newElement = createDOMElement(vDom)
      oldDom.parentNode.replaceChild(newElement, oldDom)
    }

  }
}